This notebook explores the use of SingleR
to perform cell-type annotation on datasets from the ScPCA project
SCPCP000007 (Gawad lab data).
Note: The first time you run this code it may take a
few more minutes due to reference downloads, but they will be cached for
faster future execution.
Set Up
# load the R project
project_root <- here::here()
renv::load(project_root)
* Project '~/ALSF/scpca/sc-data-integration' loaded. [renv 0.15.5]
suppressPackageStartupMessages({
library(SingleCellExperiment)
library(SingleR)
library(celldex)
library(ggplot2)
})
theme_set(theme_bw())
set.seed(params$seed) # unclear if this is doing anything? probably not. but maybe later!
utils_dir <- file.path(project_root, "scripts", "utils")
source(file.path(utils_dir, "integration-helpers.R"))
sce_file_suffix <- "processed_citeseq.rds"
integrated_sce_file <- file.path(params$integrated_sce_dir, paste0(
params$project_id, "_integrated_", params$integration_method, "_sce.rds"
))
if (!file.exists(integrated_sce_file)){
stop("Integrated SCE file could not be found.")
}
Read in the data:
library_metadata_df <- readr::read_tsv(params$library_file)
Rows: 25 Columns: 16── Column specification ─────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (15): project_name, submitter, library_biomaterial_id, sample_biomaterial_id, diagnosis, ...
lgl (1): has_CITEseq
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
integrated_sce <- readr::read_rds(integrated_sce_file)
# Define the unintegrated SCE filenames
sce_file_paths <- library_metadata_df %>%
dplyr::filter(project_name == params$project_id) %>%
dplyr::mutate(sce_file_path = file.path(
integration_input_dir, sample_biomaterial_id, glue::glue("{library_biomaterial_id}_{sce_file_suffix}")
)) %>%
dplyr::pull(sce_file_path)
SingleR
annotation
Here we performn celltype annotation with the given reference in
params$reference on each of the Gawad libraries and look at
their UMAPs colored by celltype.
umap_df <- tibble::as_tibble(reducedDim(annotation_output[["sce"]], "UMAP")) %>%
dplyr::select(UMAP1 = V1, UMAP2 = V2) %>%
dplyr::mutate(celltypes = colData(annotation_output[["sce"]])[,colname])
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
Using compatibility `.name_repair`.
Here we go!
# Read in all SCE files
sce_list <- purrr::map(
sce_file_paths,
readr::read_rds
)
# Annotate them all, popping out some viz along the way
sce_list_annotated <- purrr::map(sce_list, run_SingleR, viz = TRUE)
[1] "SCPCL000295"
[1] "SCPCL000296"
[1] "SCPCL000297"
[1] "SCPCL000298"
[1] "SCPCL000299"










# for sanity during dev:
readr::write_rds(sce_list_annotated, "sce_list_annotated.rds")
Now let’s plot, to start, the fastMNN UMAP but with celltype
annotations from individual libraries. As is deeply commented all over
the place, we definitely need to infuse palettes with biology!
# helper to count the number of celltypes across all libraries
count_celltypes <- function(sce) {
tibble::tibble(celltype = colData(sce)$SingleR_annotations ) %>%
dplyr::count(celltype) %>%
dplyr::arrange(-n) %>%
tidyr::drop_na()
}
# Celltype order based on **overall** counts for all pooled annotations
celltype_order <- purrr::map_df(sce_list_annotated, count_celltypes) %>%
dplyr::group_by(celltype) %>%
dplyr::summarize(celltype_counts = sum(n)) %>%
dplyr::arrange(-celltype_counts) %>%
dplyr::pull(celltype)
# Again we'd eventually like some biology to go into the color choices!
cell_colors <- rainbow(length(celltype_order))
# Find all the annotations for a combined color palette:
# This function is also used later for ADT extraction
extract_annotation <- function(sce, annotation_column_name) {
tibble::tibble(batch = unique(metadata(sce)$library),
celltype = colData(sce)[,annotation_column_name],
cell_barcode = rownames(colData(sce))) %>%
dplyr::mutate(cell_name = paste(cell_barcode, batch, sep = "-"))
}
# integrated UMAP with individual library cell annotations colored
purrr::map_df(sce_list_annotated, extract_annotation, "SingleR_annotations") %>%
dplyr::inner_join(
tibble::as_tibble(colData(integrated_sce), rownames = "cell_name")
) %>%
dplyr::bind_cols(
as.data.frame(reducedDim(integrated_sce, paste0(params$integration_method, "_UMAP")))
) %>%
dplyr::mutate(UMAP1 = V1, UMAP2 = V2) %>%
# And let's make a UMAP!
ggplot() +
aes(x = UMAP1, y = UMAP2, color = celltype) +
geom_point(size = 0.2, alpha = 0.3) +
# we definitely want some biology here!
scale_color_manual(values = cell_colors)
Joining, by = c("batch", "cell_name")

ADTs
After annotating, we’d like to validate with ADTs.
find_max_adt <- function(sce) {
max_adt_indices <- apply(
logcounts(altExp(sce)),
2,
which.max
)
# associated rownames which are the ADT names
max_adt <- rownames(altExp(sce))[max_adt_indices]
# add back to sce and return
sce$max_adt <- max_adt
return(sce)
}
sce_list_adt <- purrr::map(sce_list_annotated, find_max_adt)
# What are the unique ADTs for each library?
purrr::map(
lapply(sce_list_adt, `[[`, "max_adt"),
unique
)
[[1]]
[1] "CD36" "CD45" "CD33" "CD71" "CD32" "CD45RA" "CD123" "CD3" "CD90"
[10] "CD19" "CD235ab" "CD10" "CD7"
[[2]]
[1] "CD33" "CD45" "CD36" "CD10" "CD123" "CD71" "CD366" "CD45RA"
[[3]]
[1] "CD45" "CD49f" "CD71" "CD33" "CD32" "CD123" "CD10" "CD36" "CD7" "CD274"
[11] "CD45RA" "CD90" "CD3"
[[4]]
[1] "CD123" "CD45RA" "CD71" "CD45" "CD36" "CD32" "CD19" "CD7" "CD49f" "CD3"
[11] "CD10" "CD90" "CD133" "CD25" "CD366" "CD274"
[[5]]
[1] "CD45" "CD49f" "CD71" "CD32" "CD235ab" "CD90" "CD45RA" "CD7" "CD3"
[10] "CD274" "CD123" "CD25" "CD366" "CD36" "CD10" "CD279"
# What are the ADT distributions for each library?
purrr::map(
lapply(sce_list_adt, `[[`, "max_adt"),
table
)
[[1]]
CD10 CD123 CD19 CD235ab CD3 CD32 CD33 CD36 CD45 CD45RA CD7 CD71
6 366 7 68 5 836 2445 292 626 15 13 19
CD90
2
[[2]]
CD10 CD123 CD33 CD36 CD366 CD45 CD45RA CD71
2 9 2845 7 1 289 17 4
[[3]]
CD10 CD123 CD274 CD3 CD32 CD33 CD36 CD45 CD45RA CD49f CD7 CD71 CD90
6 10 4 1 56 4 1 3289 2 299 2 10 3
[[4]]
CD10 CD123 CD133 CD19 CD25 CD274 CD3 CD32 CD36 CD366 CD45 CD45RA CD49f
1 1747 1 14 4 3 12 17 31 1 726 1443 22
CD7 CD71 CD90
76 143 63
[[5]]
CD10 CD123 CD235ab CD25 CD274 CD279 CD3 CD32 CD36 CD366 CD45 CD45RA
1 18 3 4 9 4 16 5 1 1 548 1
CD49f CD7 CD71 CD90
109 14 17 38
# What's the most common ADT distributions for each library?
most_common <- function(a_list) {
sort(table(a_list), decreasing = TRUE)[1] %>%
names()
}
purrr::map(
lapply(sce_list_adt, `[[`, "max_adt"),
most_common
)
[[1]]
[1] "CD33"
[[2]]
[1] "CD33"
[[3]]
[1] "CD45"
[[4]]
[1] "CD123"
[[5]]
[1] "CD45"
- “CD33…is a transmembrane receptor expressed on cells of myeloid
lineage. It is usually considered myeloid-specific, but it can also be
found on some lymphoid cells.” source
- “CD45 is a type I transmembrane protein that is present in various
isoforms on all differentiated hematopoietic cells (except erythrocytes
and plasma cells).” source
- “The interleukin-3 receptor (CD123) is a molecule found on cells
which helps transmit the signal of interleukin-3, a soluble cytokine
important in the immune system….The receptor, found on pluripotent
progenitor cells, induces tyrosine phosphorylation within the cell and
promotes proliferation and differentiation within the hematopoietic cell
lines. It can be found on basophils and pDCs as well as some cDCs among
peripheral blood mononuclear cells….CD123 is expressed across acute
myeloid leukemia (AML) subtypes, including leukemic stem cells.” source
UMAP of the ADTs:
purrr::map_df(sce_list_adt, extract_annotation, "max_adt") %>%
dplyr::inner_join(
tibble::as_tibble(colData(integrated_sce), rownames = "cell_name")
) %>%
dplyr::bind_cols(
as.data.frame(reducedDim(integrated_sce, paste0(params$integration_method, "_UMAP")))
) %>%
dplyr::mutate(UMAP1 = V1, UMAP2 = V2) %>%
# And let's make a UMAP!
ggplot() +
aes(x = UMAP1, y = UMAP2, color = celltype) +
geom_point(size = 0.2, alpha = 0.3)
Joining, by = c("batch", "cell_name")

Unevaluated initial
exploration
Currently the code chunks in this section are not evaluated, but
remain here for now for posterity.
To build an initial sense of what we can expect annotating with
SingleR, let’s just look at one SCE (arbitrarily chosen as
the first):
# For now let's just explore one!
sce_file <- sce_file_paths[[1]]
sce <- readr::read_rds(sce_file)
sce
The celldex package contains bulk RNA-Seq datasets for
use as reference. Since this is AML data, we’ll want a reference that
has a lot of blood information. According to the celldex,
the Human Primary Cell Atlas data will be our closest
match.
The HPCA reference consists of publicly available microarray datasets
derived from human primary cells (Mabbott et al. 2013). Most of the
labels refer to blood subpopulations but cell types from other tissues
are also available.
# define reference with ensembl IDs to match our IDs
ref_se <- celldex::HumanPrimaryCellAtlasData(ensembl = TRUE)
# Predict cell types
# If we change to `labels = ref_se$label.fine`, we'll get more
# fine-grained annotations with subtypes etc.
preds_hpca <- SingleR::SingleR(
# dataset we want to annotate
test = sce,
# reference dataset
ref = ref_se,
# label.main is broad
labels = ref_se$label.main)
# Results into a tibble:
preds_df <- tibble::as_tibble(preds_hpca$labels) %>%
dplyr::rename(celltype = value) %>%
dplyr::mutate(cell_barcode = rownames(preds_hpca),
reference = "hpca")
# Save the celltypes:
hpca_celltypes <- unique(ref_se$label.main)
And a heatmap version, showing all celltypes. The bar the top shows
the final assignment, which are the rows with highest scores.
SingleR::plotScoreHeatmap(preds_hpca)
But can’t hurt to see how this compares to the
Blueprint/ENCODE reference:
The Blueprint/ENCODE reference consists of bulk RNA-seq data for pure
stroma and immune cells generated by Blueprint (Martens and Stunnenberg
2013) and ENCODE projects (The ENCODE Project Consortium 2012).
# define reference with ensembl IDs to match our IDs
ref_se <- celldex::BlueprintEncodeData(ensembl = TRUE)
# Predict cell types, broadly
preds_blue_enc <- SingleR::SingleR(
# dataset we want to annotate
test = sce,
# reference dataset
ref = ref_se,
# label.main is broad
labels = ref_se$label.main)
# Results into a tibble:
preds_df <- preds_df %>%
dplyr::bind_rows(
tibble::as_tibble(preds_blue_enc$labels) %>%
dplyr::rename(celltype = value) %>%
dplyr::mutate(cell_barcode = rownames(preds_blue_enc),
reference = "blueprint_encode")
)
# Save the celltypes:
blueprint_celltypes <- unique(ref_se$label.main)
# And the heatmap:
SingleR::plotScoreHeatmap(preds_blue_enc)
What did the references predict?
# How many of each celltype?
preds_df %>%
dplyr::filter(reference == "hpca") %>%
dplyr::count(celltype) %>%
dplyr::arrange(-n)
preds_df %>%
dplyr::filter(reference == "blueprint_encode") %>%
dplyr::count(celltype) %>%
dplyr::arrange(-n)
We’ll have to harmonize the celltype names between references to do a
robust comparison, but from a very quick glance, overlap is
thinner than one might like. That said, we don’t necessarily expect
Blueprint/ENCODE to do particularly well anyways!
For a clearer comparison, we’ll harmonize predicted celltypes, but
let’s just focus on exactly matching celltypes as a start -
# Let's see what we have here:
sort(hpca_celltypes)
sort(blueprint_celltypes)
# Manually compiled data rame of celltypes roughly present in BOTH references
# for now leaving the Pre/Pro B-cells out
shared_celltypes_df <- tibble::tribble(
~hpca_celltype, ~blueprint_celltype,
# Both references have it:
"Astrocyte", "Astrocytes",
"B_cell", "B-cells",
"Chondrocytes","Chondrocytes",
"DC", "DC",
"Endothelial_cells", "Endothelial cells",
"Epithelial_cells", "Epithelial cells",
"Fibroblasts", "Fibroblasts",
"Keratinocytes", "Keratinocytes",
"Macrophage", "Macrophages",
"Monocyte", "Monocytes",
"Neurons", "Neurons",
"Neutrophils", "Neutrophils",
"NK_cell", "NK cells",
"Smooth_muscle_cells", "Smooth muscle",
# two groups - collapse back to overall T cells
"T_cells", "CD8+ T-cells",
"T_cells", "CD4+ T-cells",
# two groups of HSCs, again, collapse back to overall
"HSC_-G-CSF", "HSC",
"HSC_CD34+", "HSC"
) %>%
dplyr::mutate(harmonized_celltype = ifelse(blueprint_celltype == "HSC",
blueprint_celltype,
hpca_celltype))
harmonize_celltypes <- function(preds_df, reference_name) {
if (reference_name == "hpca") {
shared_celltypes_colname <- rlang::sym("hpca_celltype")
} else {
shared_celltypes_colname <- rlang::sym("blueprint_celltype")
}
filtered_preds_df <- preds_df %>%
dplyr::filter(reference == reference_name)
sym_reference_name <- rlang::sym(reference_name)
filtered_preds_df %>%
dplyr::inner_join(
dplyr::select(
shared_celltypes_df,
celltype = {{shared_celltypes_colname}},
harmonized_celltype
)
) %>%
dplyr::select(cell_barcode, {{sym_reference_name}} := harmonized_celltype) %>%
dplyr::right_join(filtered_preds_df) %>%
dplyr::mutate({{reference_name}} := dplyr::if_else(
{{sym_reference_name}} %in% shared_celltypes_df$harmonized_celltype,
{{sym_reference_name}},
celltype
)) %>%
dplyr::select(-celltype, -reference)
}
preds_df_harmonized <- harmonize_celltypes(preds_df, "blueprint_encode") %>%
dplyr::left_join(
harmonize_celltypes(preds_df, "hpca")
)
# How often do they match?
preds_df_harmonized %>%
dplyr::summarize(percent_same = sum(blueprint_encode == hpca) / dplyr::n() )
# Where do they not match?
preds_df_harmonized %>%
dplyr::filter(hpca != blueprint_encode)
# Are there specific combinations that get differently annotated?
preds_df_harmonized %>%
dplyr::filter(hpca != blueprint_encode) %>%
dplyr::select(-cell_barcode) %>%
dplyr::count(blueprint_encode, hpca) %>%
dplyr::arrange(-n)
Session Info
sessionInfo()
R version 4.2.1 (2022-06-23)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Monterey 12.6.1
Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats4 stats graphics grDevices datasets utils methods base
other attached packages:
[1] ensembldb_2.20.2 AnnotationFilter_1.20.0 GenomicFeatures_1.48.3
[4] AnnotationDbi_1.58.0 magrittr_2.0.3 ggplot2_3.3.6
[7] celldex_1.6.0 SingleR_1.10.0 SingleCellExperiment_1.18.0
[10] SummarizedExperiment_1.26.1 Biobase_2.56.0 GenomicRanges_1.48.0
[13] GenomeInfoDb_1.32.3 IRanges_2.30.0 S4Vectors_0.34.0
[16] BiocGenerics_0.42.0 MatrixGenerics_1.8.1 matrixStats_0.62.0
loaded via a namespace (and not attached):
[1] colorspace_2.0-3 rjson_0.2.21 ellipsis_0.3.2
[4] modeltools_0.2-23 rprojroot_2.0.3 XVector_0.36.0
[7] BiocNeighbors_1.14.0 rstudioapi_0.13 farver_2.1.1
[10] flexmix_2.3-18 bit64_4.0.5 interactiveDisplayBase_1.34.0
[13] fansi_1.0.3 xml2_1.3.3 codetools_0.2-18
[16] splines_4.2.1 sparseMatrixStats_1.8.0 cachem_1.0.6
[19] knitr_1.39 Rsamtools_2.12.0 dbplyr_2.2.1
[22] png_0.1-7 pheatmap_1.0.12 shiny_1.7.2
[25] BiocManager_1.30.18 readr_2.1.2 compiler_4.2.1
[28] httr_1.4.3 lazyeval_0.2.2 assertthat_0.2.1
[31] Matrix_1.4-1 fastmap_1.1.0 cli_3.3.0
[34] later_1.3.0 BiocSingular_1.12.0 miQC_1.4.0
[37] htmltools_0.5.3 prettyunits_1.1.1 tools_4.2.1
[40] rsvd_1.0.5 gtable_0.3.0 glue_1.6.2
[43] GenomeInfoDbData_1.2.8 dplyr_1.0.9 rappdirs_0.3.3
[46] Rcpp_1.0.9 vctrs_0.4.1 Biostrings_2.64.0
[49] ExperimentHub_2.4.0 rtracklayer_1.56.1 DelayedMatrixStats_1.18.0
[52] xfun_0.32 stringr_1.4.0 beachmat_2.12.0
[55] mime_0.12 lifecycle_1.0.1 irlba_2.3.5
[58] restfulr_0.0.15 renv_0.15.5 XML_3.99-0.10
[61] AnnotationHub_3.4.0 zlibbioc_1.42.0 scales_1.2.0
[64] vroom_1.5.7 ProtGenerics_1.28.0 hms_1.1.1
[67] promises_1.2.0.1 parallel_4.2.1 RColorBrewer_1.1-3
[70] yaml_2.3.5 curl_4.3.2 gridExtra_2.3
[73] memoise_2.0.1 biomaRt_2.52.0 stringi_1.7.8
[76] RSQLite_2.2.15 BiocVersion_3.15.2 BiocIO_1.6.0
[79] ScaledMatrix_1.4.0 filelock_1.0.2 BiocParallel_1.30.3
[82] rlang_1.0.4 pkgconfig_2.0.3 bitops_1.0-7
[85] lattice_0.20-45 purrr_0.3.4 labeling_0.4.2
[88] GenomicAlignments_1.32.1 bit_4.0.4 tidyselect_1.1.2
[91] here_1.0.1 R6_2.5.1 generics_0.1.3
[94] DelayedArray_0.22.0 DBI_1.1.3 pillar_1.8.0
[97] withr_2.5.0 KEGGREST_1.36.3 RCurl_1.98-1.8
[100] nnet_7.3-17 tibble_3.1.8 crayon_1.5.1
[103] utf8_1.2.2 BiocFileCache_2.4.0 tzdb_0.3.0
[106] viridis_0.6.2 progress_1.2.2 grid_4.2.1
[109] blob_1.2.3 digest_0.6.29 xtable_1.8-4
[112] tidyr_1.2.0 httpuv_1.6.5 munsell_0.5.0
[115] viridisLite_0.4.0
LS0tCnBhcmFtczoKICBzZWVkOiAyMDIyCiAgbGlicmFyeV9maWxlOiAhciBmaWxlLnBhdGgoInNhbXBsZS1pbmZvIiwgInNjcGNhLXByb2Nlc3NlZC1saWJyYXJpZXMudHN2IikKICBwcm9qZWN0X2lkOiAiU0NQQ1AwMDAwMDciCiAgcmVmZXJlbmNlOiAiaHBjYSIKICBpbnRlZ3JhdGVkX3NjZV9kaXI6ICFyIGZpbGUucGF0aCgicmVzdWx0cyIsICJzY3BjYSIsICJpbnRlZ3JhdGVkX3NjZSIpCiAgaW50ZWdyYXRpb25fbWV0aG9kOiAiZmFzdG1ubiIKICBtYXhfY2VsbHR5cGVzOiA1CnRpdGxlOiAiQ2VsbCB0eXBlIGFubm90YXRpb24gZXhwbG9yYXRpb24iCmF1dGhvcjogIkRhdGEgTGFiIgpkYXRlOiAiYHIgcGFyYW1zJGRhdGVgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KClRoaXMgbm90ZWJvb2sgZXhwbG9yZXMgdGhlIHVzZSBvZiBbYFNpbmdsZVJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvcmVsZWFzZS9TaW5nbGVSQm9vay8pIHRvIHBlcmZvcm0gY2VsbC10eXBlIGFubm90YXRpb24gb24gZGF0YXNldHMgZnJvbSB0aGUgU2NQQ0EgcHJvamVjdCBTQ1BDUDAwMDAwNyAoR2F3YWQgbGFiIGRhdGEpLgoKKipOb3RlOioqIFRoZSBmaXJzdCB0aW1lIHlvdSBydW4gdGhpcyBjb2RlIGl0IG1heSB0YWtlIGEgZmV3IG1vcmUgbWludXRlcyBkdWUgdG8gcmVmZXJlbmNlIGRvd25sb2FkcywgYnV0IHRoZXkgd2lsbCBiZSBjYWNoZWQgZm9yIGZhc3RlciBmdXR1cmUgZXhlY3V0aW9uLgoKIyMgU2V0IFVwCgpgYGB7ciBzZXR1cH0KIyBsb2FkIHRoZSBSIHByb2plY3QKcHJvamVjdF9yb290IDwtIGhlcmU6OmhlcmUoKQpyZW52Ojpsb2FkKHByb2plY3Rfcm9vdCkKCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKICBsaWJyYXJ5KFNpbmdsZVIpCiAgbGlicmFyeShjZWxsZGV4KQogIGxpYnJhcnkoZ2dwbG90MikKfSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCnNldC5zZWVkKHBhcmFtcyRzZWVkKSAjIHVuY2xlYXIgaWYgdGhpcyBpcyBkb2luZyBhbnl0aGluZz8gcHJvYmFibHkgbm90LiBidXQgbWF5YmUgbGF0ZXIhCgp1dGlsc19kaXIgPC0gZmlsZS5wYXRoKHByb2plY3Rfcm9vdCwgInNjcmlwdHMiLCAidXRpbHMiKQpzb3VyY2UoZmlsZS5wYXRoKHV0aWxzX2RpciwgImludGVncmF0aW9uLWhlbHBlcnMuUiIpKQoKc2NlX2ZpbGVfc3VmZml4IDwtICJwcm9jZXNzZWRfY2l0ZXNlcS5yZHMiCgppbnRlZ3JhdGVkX3NjZV9maWxlIDwtIGZpbGUucGF0aChwYXJhbXMkaW50ZWdyYXRlZF9zY2VfZGlyLCBwYXN0ZTAoCiAgcGFyYW1zJHByb2plY3RfaWQsICJfaW50ZWdyYXRlZF8iLCBwYXJhbXMkaW50ZWdyYXRpb25fbWV0aG9kLCAiX3NjZS5yZHMiCikpCmlmICghZmlsZS5leGlzdHMoaW50ZWdyYXRlZF9zY2VfZmlsZSkpewogIHN0b3AoIkludGVncmF0ZWQgU0NFIGZpbGUgY291bGQgbm90IGJlIGZvdW5kLiIpCn0KYGBgCgpSZWFkIGluIHRoZSBkYXRhOgoKYGBge3J9CmxpYnJhcnlfbWV0YWRhdGFfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHBhcmFtcyRsaWJyYXJ5X2ZpbGUpCgppbnRlZ3JhdGVkX3NjZSA8LSByZWFkcjo6cmVhZF9yZHMoaW50ZWdyYXRlZF9zY2VfZmlsZSkKCiMgRGVmaW5lIHRoZSB1bmludGVncmF0ZWQgU0NFIGZpbGVuYW1lcwpzY2VfZmlsZV9wYXRocyA8LSBsaWJyYXJ5X21ldGFkYXRhX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIocHJvamVjdF9uYW1lID09IHBhcmFtcyRwcm9qZWN0X2lkKSAlPiUKICBkcGx5cjo6bXV0YXRlKHNjZV9maWxlX3BhdGggPSBmaWxlLnBhdGgoCiAgICBpbnRlZ3JhdGlvbl9pbnB1dF9kaXIsIHNhbXBsZV9iaW9tYXRlcmlhbF9pZCwgZ2x1ZTo6Z2x1ZSgie2xpYnJhcnlfYmlvbWF0ZXJpYWxfaWR9X3tzY2VfZmlsZV9zdWZmaXh9IikKICApKSAlPiUKICBkcGx5cjo6cHVsbChzY2VfZmlsZV9wYXRoKQoKCmBgYAoKCiMjIGBTaW5nbGVSYCBhbm5vdGF0aW9uCgpIZXJlIHdlIHBlcmZvcm1uIGNlbGx0eXBlIGFubm90YXRpb24gd2l0aCB0aGUgZ2l2ZW4gcmVmZXJlbmNlIGluIGBwYXJhbXMkcmVmZXJlbmNlYCBvbiBlYWNoIG9mIHRoZSBHYXdhZCBsaWJyYXJpZXMgYW5kIGxvb2sgYXQgdGhlaXIgVU1BUHMgY29sb3JlZCBieSBjZWxsdHlwZS4KCmBgYHtyfQppZiAocGFyYW1zJHJlZmVyZW5jZSA9PSAiaHBjYSIpIHsKICByZWZfZGF0YSA8LSBjZWxsZGV4OjpIdW1hblByaW1hcnlDZWxsQXRsYXNEYXRhKGVuc2VtYmwgPSBUUlVFKQp9IGVsc2UgaWYgKHBhcmFtcyRyZWZlcmVuY2UgPT0gImJsdWVwcmludF9lbmNvZGUiKSB7CiAgcmVmX2RhdGEgPC0gY2VsbGRleDo6Qmx1ZXByaW50RW5jb2RlRGF0YShlbnNlbWJsID0gVFJVRSkKfSBlbHNlIHsKICBzdG9wKCJCYWQgcmVmZXJlbmNlIHBhcmFtZXRlcjsgZWl0aGVyICdocGNhJyBvciAnYmx1ZXByaW50X2VuY29kZScuIikKfQoKCmFubm90YXRlX1NpbmdsZVIgPC0gZnVuY3Rpb24oc2NlLCByZWZfZGF0YSkgewogICMgcmV0dXJuIHVwZGF0ZWQgc2NlIGFuZCB0aGUgcHJlZGljdGlvbnMgdGhlbXNlbHZlcwogIAogIHByZWRzIDwtIFNpbmdsZVI6OlNpbmdsZVIoCiAgICB0ZXN0ID0gc2NlLCAKICAgIHJlZiA9IHJlZl9kYXRhLAogICAgbGFiZWxzID0gcmVmX2RhdGEkbGFiZWwubWFpbgogICkKICAKICAjIEFkZCBgcHJ1bmVkLmxhYmVsc2AsIHdoZXJlIGxvdy1jb25maWRlbmNlIGFubm90YXRpb25zIGFyZSBOQSwgdG8gc2NlCiAgIHNjZSRTaW5nbGVSX2Fubm90YXRpb25zIDwtIHByZWRzJHBydW5lZC5sYWJlbHMKICAKICAgcmV0dXJuKAogICAgIGxpc3QoCiAgICAgICBzY2UgPSBzY2UsCiAgICAgICBwcmVkcyA9IHByZWRzCiAgICAgICApCiAgICkKfQoKcGxvdF9TaW5nbGVSIDwtIGZ1bmN0aW9uKGFubm90YXRpb25fb3V0cHV0LCBjb2xuYW1lKSB7CiAgIyBhbm5vdGF0aW9uX291dHB1dDogbGlzdCBvZiBzY2UgYW5kIHByZWRzCiAgIyBjb2xuYW1lOiBjb2x1bW4gbmFtZSB0byBwbG90CiAgIyBNYWtlIGEgaGVhdG1hcCBhbmQgVU1BUCBhbmQgcHJpbnQgb3V0CiAgCiAgaGVhdG1hcCA8LSBTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKGFubm90YXRpb25fb3V0cHV0W1sicHJlZHMiXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRlZmF1bHQgYnV0IGxldCdzIGJlIGV4cGxpY2l0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3cucHJ1bmVkID0gRkFMU0UpCgogIAogICMgSW4gY2FzZSwgZm9yIGBsZXZlbHMoKWAgYmVsb3cuCiAgY29sRGF0YShhbm5vdGF0aW9uX291dHB1dFtbInNjZSJdXSlbLGNvbG5hbWVdIDwtIGFzLmZhY3Rvcihjb2xEYXRhKGFubm90YXRpb25fb3V0cHV0W1sic2NlIl1dKVssY29sbmFtZV0pCiAgCiAgdW1hcF9kZiA8LSB0aWJibGU6OmFzX3RpYmJsZShyZWR1Y2VkRGltKGFubm90YXRpb25fb3V0cHV0W1sic2NlIl1dLCAiVU1BUCIpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoVU1BUDEgPSBWMSwgVU1BUDIgPSBWMikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGx0eXBlcyA9IGNvbERhdGEoYW5ub3RhdGlvbl9vdXRwdXRbWyJzY2UiXV0pWyxjb2xuYW1lXSkKICAKICAjIFdlIHdvdWxkIHByb2JhYmx5IGxpa2UgYSBtb3JlIHByaW5jaXBsZWQgYXBwcm9hY2ggdG8gY29sb3JzIHRoYXQgaXMKICAjICBhY3R1YWxseSBiYXNlZCBvbiBiaW9sb2dpY2FsIGluZm9ybWF0aW9uIGFib3V0IGNlbGx0eXBlcwogIHBsb3RfY29sb3JzIDwtIHJhaW5ib3coIGxlbmd0aChsZXZlbHModW1hcF9kZiRjZWxsdHlwZXMpKSApCiAgCiAgdW1hcCA8LSBnZ3Bsb3QodW1hcF9kZikgKyAKICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBjZWxsdHlwZXMpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuMiwgYWxwaGEgPSAwLjUpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGxvdF9jb2xvcnMpICsKICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MykpKSArCiAgICBnZ3RpdGxlKGdsdWU6OmdsdWUoIlVNQVAgd2l0aCB0b3Age3BhcmFtcyRtYXhfY2VsbHR5cGVzfSBjZWxsdHlwZXMgc2hvd24iKSkKICAKICBwcmludChoZWF0bWFwKQogIHByaW50KHVtYXApCiAgCn0KCiMgRnVuY3Rpb24gdG8gcnVuIGFuZCBwbG90IFNpbmdsZVIKIyBSZXR1cm4gU0NFIHdpdGggYW5ub3RhdGlvbnMgYFNpbmdsZVJfYW5ub3RhdGlvbnNgIGNvbHVtbgpydW5fU2luZ2xlUiA8LSBmdW5jdGlvbihzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICBtYXhfY2VsbHR5cGVzID0gcGFyYW1zJG1heF9jZWxsdHlwZXMsIAogICAgICAgICAgICAgICAgICAgICAgICB2aXogPSBUUlVFKSB7CiAgIyBQcmludCBvdXQgdGhlIGxpYnJhcnkKICBwcmludCh1bmlxdWUoIG1ldGFkYXRhKHNjZSkkbGlicmFyeSApKQogIAogICMgUnVuIGFubm90YXRpb25zCiAgYW5ubyA8LSBhbm5vdGF0ZV9TaW5nbGVSKHNjZSwgcmVmX2RhdGEpCiAgCiAgIyBDcmVhdGUgYW4gYWRkaXRpb25hbCBjb2x1bW4gYFNpbmdsZVJfYW5ub3RhdGlvbnNfY29sbGFwc2VkYCB3aXRoIG9ubHkgbWF4X2NlbGx0eXBlcwogIGFubm9bWyJzY2UiXV0kU2luZ2xlUl9hbm5vdGF0aW9uc19jb2xsYXBzZWQgPC0gZm9yY2F0czo6ZmN0X2x1bXBfbigKICAgIGFubm9bWyJzY2UiXV0kU2luZ2xlUl9hbm5vdGF0aW9ucywgCiAgICBtYXhfY2VsbHR5cGVzCiAgKQogIAogICMgcGxvdCBpZiBUUlVFCiAgaWYgKHZpeikgewogICAgIyB0aGVyZSBpcyBubyBpbnRlcmVzdGluZyBjb2xvciBwYWxldHRlIGhhcm1vbml6YXRpb24gYW1vbmcgbGlicmFyeSBjb2xvcnMgaGVyZSEKICAgIHBsb3RfU2luZ2xlUihhbm5vLCAiU2luZ2xlUl9hbm5vdGF0aW9uc19jb2xsYXBzZWQiKQogIH0KICByZXR1cm4oYW5ub1tbInNjZSJdXSkKfQpgYGAKCkhlcmUgd2UgZ28hCgpgYGB7cn0KIyBSZWFkIGluIGFsbCBTQ0UgZmlsZXMKc2NlX2xpc3QgPC0gcHVycnI6Om1hcCgKICBzY2VfZmlsZV9wYXRocywgCiAgcmVhZHI6OnJlYWRfcmRzCikKCiMgQW5ub3RhdGUgdGhlbSBhbGwsIHBvcHBpbmcgb3V0IHNvbWUgdml6IGFsb25nIHRoZSB3YXkKc2NlX2xpc3RfYW5ub3RhdGVkIDwtIHB1cnJyOjptYXAoc2NlX2xpc3QsIHJ1bl9TaW5nbGVSLCB2aXogPSBUUlVFKQoKIyBmb3Igc2FuaXR5IGR1cmluZyBkZXY6CnJlYWRyOjp3cml0ZV9yZHMoc2NlX2xpc3RfYW5ub3RhdGVkLCAic2NlX2xpc3RfYW5ub3RhdGVkLnJkcyIpCmBgYAoKCgpOb3cgbGV0J3MgcGxvdCwgdG8gc3RhcnQsIHRoZSBmYXN0TU5OIFVNQVAgYnV0IHdpdGggY2VsbHR5cGUgYW5ub3RhdGlvbnMgZnJvbSBpbmRpdmlkdWFsIGxpYnJhcmllcy4KQXMgaXMgZGVlcGx5IGNvbW1lbnRlZCBhbGwgb3ZlciB0aGUgcGxhY2UsIHdlIGRlZmluaXRlbHkgbmVlZCB0byBpbmZ1c2UgcGFsZXR0ZXMgd2l0aCBiaW9sb2d5IQoKYGBge3J9CiMgaGVscGVyIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgY2VsbHR5cGVzIGFjcm9zcyBhbGwgbGlicmFyaWVzCmNvdW50X2NlbGx0eXBlcyA8LSBmdW5jdGlvbihzY2UpIHsKIHRpYmJsZTo6dGliYmxlKGNlbGx0eXBlID0gY29sRGF0YShzY2UpJFNpbmdsZVJfYW5ub3RhdGlvbnMgKSAlPiUKICAgIGRwbHlyOjpjb3VudChjZWxsdHlwZSkgJT4lCiAgICBkcGx5cjo6YXJyYW5nZSgtbikgJT4lCiAgICB0aWR5cjo6ZHJvcF9uYSgpIAp9CgojIENlbGx0eXBlIG9yZGVyIGJhc2VkIG9uICoqb3ZlcmFsbCoqIGNvdW50cyBmb3IgYWxsIHBvb2xlZCBhbm5vdGF0aW9ucyAKY2VsbHR5cGVfb3JkZXIgPC0gcHVycnI6Om1hcF9kZihzY2VfbGlzdF9hbm5vdGF0ZWQsIGNvdW50X2NlbGx0eXBlcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGNlbGx0eXBlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKGNlbGx0eXBlX2NvdW50cyA9IHN1bShuKSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLWNlbGx0eXBlX2NvdW50cykgJT4lCiAgZHBseXI6OnB1bGwoY2VsbHR5cGUpCgoKIyBBZ2FpbiB3ZSdkIGV2ZW50dWFsbHkgbGlrZSBzb21lIGJpb2xvZ3kgdG8gZ28gaW50byB0aGUgY29sb3IgY2hvaWNlcyEKY2VsbF9jb2xvcnMgPC0gcmFpbmJvdyhsZW5ndGgoY2VsbHR5cGVfb3JkZXIpKQoKCgojIEZpbmQgYWxsIHRoZSBhbm5vdGF0aW9ucyBmb3IgYSBjb21iaW5lZCBjb2xvciBwYWxldHRlOgojIFRoaXMgZnVuY3Rpb24gaXMgYWxzbyB1c2VkIGxhdGVyIGZvciBBRFQgZXh0cmFjdGlvbgpleHRyYWN0X2Fubm90YXRpb24gPC0gZnVuY3Rpb24oc2NlLCBhbm5vdGF0aW9uX2NvbHVtbl9uYW1lKSB7CiB0aWJibGU6OnRpYmJsZShiYXRjaCA9IHVuaXF1ZShtZXRhZGF0YShzY2UpJGxpYnJhcnkpLAogICAgICAgICAgICAgICAgY2VsbHR5cGUgPSBjb2xEYXRhKHNjZSlbLGFubm90YXRpb25fY29sdW1uX25hbWVdLCAKICAgICAgICAgICAgICAgIGNlbGxfYmFyY29kZSA9IHJvd25hbWVzKGNvbERhdGEoc2NlKSkpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX25hbWUgPSBwYXN0ZShjZWxsX2JhcmNvZGUsIGJhdGNoLCBzZXAgPSAiLSIpKQp9CgoKIyBpbnRlZ3JhdGVkIFVNQVAgd2l0aCBpbmRpdmlkdWFsIGxpYnJhcnkgY2VsbCBhbm5vdGF0aW9ucyBjb2xvcmVkCnB1cnJyOjptYXBfZGYoc2NlX2xpc3RfYW5ub3RhdGVkLCBleHRyYWN0X2Fubm90YXRpb24sICJTaW5nbGVSX2Fubm90YXRpb25zIikgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4oCiAgICB0aWJibGU6OmFzX3RpYmJsZShjb2xEYXRhKGludGVncmF0ZWRfc2NlKSwgcm93bmFtZXMgPSAiY2VsbF9uYW1lIikKICApICU+JQogIGRwbHlyOjpiaW5kX2NvbHMoCiAgICBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oaW50ZWdyYXRlZF9zY2UsIHBhc3RlMChwYXJhbXMkaW50ZWdyYXRpb25fbWV0aG9kLCAiX1VNQVAiKSkpCiAgKSAlPiUKICBkcGx5cjo6bXV0YXRlKFVNQVAxID0gVjEsIFVNQVAyID0gVjIpICU+JQogICMgQW5kIGxldCdzIG1ha2UgYSBVTUFQIQogIGdncGxvdCgpICsKICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gY2VsbHR5cGUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC4yLCBhbHBoYSA9IDAuMykgKyAKICAjIHdlIGRlZmluaXRlbHkgd2FudCBzb21lIGJpb2xvZ3kgaGVyZSEKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2VsbF9jb2xvcnMpCmBgYAoKCgojIyBBRFRzCgpBZnRlciBhbm5vdGF0aW5nLCB3ZSdkIGxpa2UgdG8gdmFsaWRhdGUgd2l0aCBBRFRzLgoKYGBge3J9CmZpbmRfbWF4X2FkdCA8LSBmdW5jdGlvbihzY2UpIHsKICBtYXhfYWR0X2luZGljZXMgPC0gYXBwbHkoCiAgICBsb2djb3VudHMoYWx0RXhwKHNjZSkpLAogICAgMiwKICAgIHdoaWNoLm1heAogICkKICAjIGFzc29jaWF0ZWQgcm93bmFtZXMgd2hpY2ggYXJlIHRoZSBBRFQgbmFtZXMKICBtYXhfYWR0IDwtIHJvd25hbWVzKGFsdEV4cChzY2UpKVttYXhfYWR0X2luZGljZXNdCiAgCiAgIyBhZGQgYmFjayB0byBzY2UgYW5kIHJldHVybgogIHNjZSRtYXhfYWR0IDwtIG1heF9hZHQKICByZXR1cm4oc2NlKQp9CgoKc2NlX2xpc3RfYWR0IDwtIHB1cnJyOjptYXAoc2NlX2xpc3RfYW5ub3RhdGVkLCBmaW5kX21heF9hZHQpCgoKIyBXaGF0IGFyZSB0aGUgdW5pcXVlIEFEVHMgZm9yIGVhY2ggbGlicmFyeT8KcHVycnI6Om1hcCgKICBsYXBwbHkoc2NlX2xpc3RfYWR0LCBgW1tgLCAibWF4X2FkdCIpLCAKICB1bmlxdWUKKQoKIyBXaGF0IGFyZSB0aGUgQURUIGRpc3RyaWJ1dGlvbnMgZm9yIGVhY2ggbGlicmFyeT8KcHVycnI6Om1hcCgKICBsYXBwbHkoc2NlX2xpc3RfYWR0LCBgW1tgLCAibWF4X2FkdCIpLCAKICB0YWJsZQopCgojIFdoYXQncyB0aGUgbW9zdCBjb21tb24gQURUIGRpc3RyaWJ1dGlvbnMgZm9yIGVhY2ggbGlicmFyeT8KbW9zdF9jb21tb24gPC0gZnVuY3Rpb24oYV9saXN0KSB7CiAgc29ydCh0YWJsZShhX2xpc3QpLCBkZWNyZWFzaW5nID0gVFJVRSlbMV0gJT4lCiAgICBuYW1lcygpCn0KcHVycnI6Om1hcCgKICBsYXBwbHkoc2NlX2xpc3RfYWR0LCBgW1tgLCAibWF4X2FkdCIpLCAKICBtb3N0X2NvbW1vbgopCgpgYGAKCi0gIkNEMzMuLi5pcyBhIHRyYW5zbWVtYnJhbmUgcmVjZXB0b3IgZXhwcmVzc2VkIG9uIGNlbGxzIG9mIG15ZWxvaWQgbGluZWFnZS4gSXQgaXMgdXN1YWxseSBjb25zaWRlcmVkIG15ZWxvaWQtc3BlY2lmaWMsIGJ1dCBpdCBjYW4gYWxzbyBiZSBmb3VuZCBvbiBzb21lIGx5bXBob2lkIGNlbGxzLiIgW3NvdXJjZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ0QzMykKLSAiQ0Q0NSBpcyBhIHR5cGUgSSB0cmFuc21lbWJyYW5lIHByb3RlaW4gdGhhdCBpcyBwcmVzZW50IGluIHZhcmlvdXMgaXNvZm9ybXMgb24gYWxsIGRpZmZlcmVudGlhdGVkIGhlbWF0b3BvaWV0aWMgY2VsbHMgKGV4Y2VwdCBlcnl0aHJvY3l0ZXMgYW5kIHBsYXNtYSBjZWxscykuIiBbc291cmNlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DRDQ1KQotICJUaGUgaW50ZXJsZXVraW4tMyByZWNlcHRvciAoQ0QxMjMpIGlzIGEgbW9sZWN1bGUgZm91bmQgb24gY2VsbHMgd2hpY2ggaGVscHMgdHJhbnNtaXQgdGhlIHNpZ25hbCBvZiBpbnRlcmxldWtpbi0zLCBhIHNvbHVibGUgY3l0b2tpbmUgaW1wb3J0YW50IGluIHRoZSBpbW11bmUgc3lzdGVtLi4uLlRoZSByZWNlcHRvciwgZm91bmQgb24gcGx1cmlwb3RlbnQgcHJvZ2VuaXRvciBjZWxscywgaW5kdWNlcyB0eXJvc2luZSBwaG9zcGhvcnlsYXRpb24gd2l0aGluIHRoZSBjZWxsIGFuZCBwcm9tb3RlcyBwcm9saWZlcmF0aW9uIGFuZCBkaWZmZXJlbnRpYXRpb24gd2l0aGluIHRoZSBoZW1hdG9wb2lldGljIGNlbGwgbGluZXMuIEl0IGNhbiBiZSBmb3VuZCBvbiBiYXNvcGhpbHMgYW5kIHBEQ3MgYXMgd2VsbCBhcyBzb21lIGNEQ3MgYW1vbmcgcGVyaXBoZXJhbCBibG9vZCBtb25vbnVjbGVhciBjZWxscy4uLi5DRDEyMyBpcyBleHByZXNzZWQgYWNyb3NzIGFjdXRlIG15ZWxvaWQgbGV1a2VtaWEgKEFNTCkgc3VidHlwZXMsIGluY2x1ZGluZyBsZXVrZW1pYyBzdGVtIGNlbGxzLiIgW3NvdXJjZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSW50ZXJsZXVraW4tM19yZWNlcHRvcikKCgpVTUFQIG9mIHRoZSBBRFRzOgoKYGBge3J9CnB1cnJyOjptYXBfZGYoc2NlX2xpc3RfYWR0LCBleHRyYWN0X2Fubm90YXRpb24sICJtYXhfYWR0IikgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4oCiAgICB0aWJibGU6OmFzX3RpYmJsZShjb2xEYXRhKGludGVncmF0ZWRfc2NlKSwgcm93bmFtZXMgPSAiY2VsbF9uYW1lIikKICApICU+JQogIGRwbHlyOjpiaW5kX2NvbHMoCiAgICBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oaW50ZWdyYXRlZF9zY2UsIHBhc3RlMChwYXJhbXMkaW50ZWdyYXRpb25fbWV0aG9kLCAiX1VNQVAiKSkpCiAgKSAlPiUKICBkcGx5cjo6bXV0YXRlKFVNQVAxID0gVjEsIFVNQVAyID0gVjIpICU+JQogICMgQW5kIGxldCdzIG1ha2UgYSBVTUFQIQogIGdncGxvdCgpICsKICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gY2VsbHR5cGUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMC4yLCBhbHBoYSA9IDAuMykKYGBgCgoKCgojIyBVbmV2YWx1YXRlZCBpbml0aWFsIGV4cGxvcmF0aW9uCgo+IEN1cnJlbnRseSB0aGUgY29kZSBjaHVua3MgaW4gdGhpcyBzZWN0aW9uIGFyZSBub3QgZXZhbHVhdGVkLCBidXQgcmVtYWluIGhlcmUgZm9yIG5vdyBmb3IgcG9zdGVyaXR5LgoKVG8gYnVpbGQgYW4gaW5pdGlhbCBzZW5zZSBvZiB3aGF0IHdlIGNhbiBleHBlY3QgYW5ub3RhdGluZyB3aXRoIGBTaW5nbGVSYCwgbGV0J3MganVzdCBsb29rIGF0IG9uZSBTQ0UgKGFyYml0cmFyaWx5IGNob3NlbiBhcyB0aGUgZmlyc3QpOgoKYGBge3IsIGV2YWw9Rn0KIyBGb3Igbm93IGxldCdzIGp1c3QgZXhwbG9yZSBvbmUhCnNjZV9maWxlIDwtIHNjZV9maWxlX3BhdGhzW1sxXV0KCnNjZSA8LSByZWFkcjo6cmVhZF9yZHMoc2NlX2ZpbGUpCnNjZQpgYGAKCgpUaGUgYGNlbGxkZXhgIHBhY2thZ2UgY29udGFpbnMgYnVsayBSTkEtU2VxIGRhdGFzZXRzIGZvciB1c2UgYXMgcmVmZXJlbmNlLgpTaW5jZSB0aGlzIGlzIEFNTCBkYXRhLCB3ZSdsbCB3YW50IGEgcmVmZXJlbmNlIHRoYXQgaGFzIGEgbG90IG9mIGJsb29kIGluZm9ybWF0aW9uLgpBY2NvcmRpbmcgdG8gdGhlIFtgY2VsbGRleGBdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2RhdGEvZXhwZXJpbWVudC92aWduZXR0ZXMvY2VsbGRleC9pbnN0L2RvYy91c2VyZ3VpZGUuaHRtbCksIHRoZSBgSHVtYW4gUHJpbWFyeSBDZWxsIEF0bGFzYCBkYXRhIHdpbGwgYmUgb3VyIGNsb3Nlc3QgbWF0Y2guCgo+IFRoZSBIUENBIHJlZmVyZW5jZSBjb25zaXN0cyBvZiBwdWJsaWNseSBhdmFpbGFibGUgbWljcm9hcnJheSBkYXRhc2V0cyBkZXJpdmVkIGZyb20gaHVtYW4gcHJpbWFyeSBjZWxscyAoTWFiYm90dCBldCBhbC4gMjAxMykuIE1vc3Qgb2YgdGhlIGxhYmVscyByZWZlciB0byBibG9vZCBzdWJwb3B1bGF0aW9ucyBidXQgY2VsbCB0eXBlcyBmcm9tIG90aGVyIHRpc3N1ZXMgYXJlIGFsc28gYXZhaWxhYmxlLgoKCmBgYHtyLCBldmFsPUZ9CiMgZGVmaW5lIHJlZmVyZW5jZSB3aXRoIGVuc2VtYmwgSURzIHRvIG1hdGNoIG91ciBJRHMKcmVmX3NlIDwtIGNlbGxkZXg6Okh1bWFuUHJpbWFyeUNlbGxBdGxhc0RhdGEoZW5zZW1ibCA9IFRSVUUpCgojIFByZWRpY3QgY2VsbCB0eXBlcwojICBJZiB3ZSBjaGFuZ2UgdG8gYGxhYmVscyA9IHJlZl9zZSRsYWJlbC5maW5lYCwgd2UnbGwgZ2V0IG1vcmUgCiMgIGZpbmUtZ3JhaW5lZCBhbm5vdGF0aW9ucyB3aXRoIHN1YnR5cGVzIGV0Yy4KcHJlZHNfaHBjYSA8LSBTaW5nbGVSOjpTaW5nbGVSKAogICMgZGF0YXNldCB3ZSB3YW50IHRvIGFubm90YXRlCiAgdGVzdCA9IHNjZSwgCiAgIyByZWZlcmVuY2UgZGF0YXNldAogIHJlZiA9IHJlZl9zZSwKICAjIGxhYmVsLm1haW4gaXMgYnJvYWQgCiAgbGFiZWxzID0gcmVmX3NlJGxhYmVsLm1haW4pCgoKIyBSZXN1bHRzIGludG8gYSB0aWJibGU6CnByZWRzX2RmIDwtIHRpYmJsZTo6YXNfdGliYmxlKHByZWRzX2hwY2EkbGFiZWxzKSAlPiUKICBkcGx5cjo6cmVuYW1lKGNlbGx0eXBlID0gdmFsdWUpICU+JQogIGRwbHlyOjptdXRhdGUoY2VsbF9iYXJjb2RlID0gcm93bmFtZXMocHJlZHNfaHBjYSksCiAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSAiaHBjYSIpCgojIFNhdmUgdGhlIGNlbGx0eXBlczoKaHBjYV9jZWxsdHlwZXMgPC0gdW5pcXVlKHJlZl9zZSRsYWJlbC5tYWluKQpgYGAKCkFuZCBhIGhlYXRtYXAgdmVyc2lvbiwgc2hvd2luZyBhbGwgY2VsbHR5cGVzLiAKVGhlIGJhciB0aGUgdG9wIHNob3dzIHRoZSBmaW5hbCBhc3NpZ25tZW50LCB3aGljaCBhcmUgdGhlIHJvd3Mgd2l0aCBoaWdoZXN0IHNjb3Jlcy4KCmBgYHtyLCBldmFsPUYsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKHByZWRzX2hwY2EpCmBgYAoKQnV0IGNhbid0IGh1cnQgdG8gc2VlIGhvdyB0aGlzIGNvbXBhcmVzIHRvIHRoZSBgQmx1ZXByaW50L0VOQ09ERWAgcmVmZXJlbmNlOgoKPiBUaGUgQmx1ZXByaW50L0VOQ09ERSByZWZlcmVuY2UgY29uc2lzdHMgb2YgYnVsayBSTkEtc2VxIGRhdGEgZm9yIHB1cmUgc3Ryb21hIGFuZCBpbW11bmUgY2VsbHMgZ2VuZXJhdGVkIGJ5IEJsdWVwcmludCAoTWFydGVucyBhbmQgU3R1bm5lbmJlcmcgMjAxMykgYW5kIEVOQ09ERSBwcm9qZWN0cyAoVGhlIEVOQ09ERSBQcm9qZWN0IENvbnNvcnRpdW0gMjAxMikuCgoKYGBge3IsIGV2YWw9RiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CiMgZGVmaW5lIHJlZmVyZW5jZSB3aXRoIGVuc2VtYmwgSURzIHRvIG1hdGNoIG91ciBJRHMKcmVmX3NlIDwtIGNlbGxkZXg6OkJsdWVwcmludEVuY29kZURhdGEoZW5zZW1ibCA9IFRSVUUpCgojIFByZWRpY3QgY2VsbCB0eXBlcywgYnJvYWRseQpwcmVkc19ibHVlX2VuYyA8LSBTaW5nbGVSOjpTaW5nbGVSKAogICMgZGF0YXNldCB3ZSB3YW50IHRvIGFubm90YXRlCiAgdGVzdCA9IHNjZSwgCiAgIyByZWZlcmVuY2UgZGF0YXNldAogIHJlZiA9IHJlZl9zZSwKICAjIGxhYmVsLm1haW4gaXMgYnJvYWQgCiAgbGFiZWxzID0gcmVmX3NlJGxhYmVsLm1haW4pCgoKIyBSZXN1bHRzIGludG8gYSB0aWJibGU6CnByZWRzX2RmIDwtIHByZWRzX2RmICU+JQogIGRwbHlyOjpiaW5kX3Jvd3MoCiAgICB0aWJibGU6OmFzX3RpYmJsZShwcmVkc19ibHVlX2VuYyRsYWJlbHMpICU+JQogICAgZHBseXI6OnJlbmFtZShjZWxsdHlwZSA9IHZhbHVlKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY2VsbF9iYXJjb2RlID0gcm93bmFtZXMocHJlZHNfYmx1ZV9lbmMpLAogICAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSAiYmx1ZXByaW50X2VuY29kZSIpCiAgKQoKIyBTYXZlIHRoZSBjZWxsdHlwZXM6CmJsdWVwcmludF9jZWxsdHlwZXMgPC0gdW5pcXVlKHJlZl9zZSRsYWJlbC5tYWluKQoKIyBBbmQgdGhlIGhlYXRtYXA6ClNpbmdsZVI6OnBsb3RTY29yZUhlYXRtYXAocHJlZHNfYmx1ZV9lbmMpCmBgYAoKCldoYXQgZGlkIHRoZSByZWZlcmVuY2VzIHByZWRpY3Q/CgoKYGBge3IsIGV2YWw9Rn0KIyBIb3cgbWFueSBvZiBlYWNoIGNlbGx0eXBlPwpwcmVkc19kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKHJlZmVyZW5jZSA9PSAiaHBjYSIpICU+JQogIGRwbHlyOjpjb3VudChjZWxsdHlwZSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLW4pCgoKcHJlZHNfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihyZWZlcmVuY2UgPT0gImJsdWVwcmludF9lbmNvZGUiKSAlPiUKICBkcGx5cjo6Y291bnQoY2VsbHR5cGUpICU+JQogIGRwbHlyOjphcnJhbmdlKC1uKQpgYGAKCldlJ2xsIGhhdmUgdG8gaGFybW9uaXplIHRoZSBjZWxsdHlwZSBuYW1lcyBiZXR3ZWVuIHJlZmVyZW5jZXMgdG8gZG8gYSByb2J1c3QgY29tcGFyaXNvbiwgYnV0IGZyb20gYSBfdmVyeV8gcXVpY2sgZ2xhbmNlLCBvdmVybGFwIGlzIHRoaW5uZXIgdGhhbiBvbmUgbWlnaHQgbGlrZS4KVGhhdCBzYWlkLCB3ZSBkb24ndCBuZWNlc3NhcmlseSBleHBlY3QgQmx1ZXByaW50L0VOQ09ERSB0byBkbyBwYXJ0aWN1bGFybHkgd2VsbCBhbnl3YXlzIQoKCkZvciBhIGNsZWFyZXIgY29tcGFyaXNvbiwgd2UnbGwgaGFybW9uaXplIHByZWRpY3RlZCBjZWxsdHlwZXMsIGJ1dCBsZXQncyBqdXN0IGZvY3VzIG9uIGV4YWN0bHkgbWF0Y2hpbmcgY2VsbHR5cGVzIGFzIGEgc3RhcnQgLSAKYGBge3IsIGV2YWw9Rn0KIyBMZXQncyBzZWUgd2hhdCB3ZSBoYXZlIGhlcmU6CnNvcnQoaHBjYV9jZWxsdHlwZXMpCnNvcnQoYmx1ZXByaW50X2NlbGx0eXBlcykKCiMgTWFudWFsbHkgY29tcGlsZWQgZGF0YSByYW1lIG9mIGNlbGx0eXBlcyByb3VnaGx5IHByZXNlbnQgaW4gQk9USCByZWZlcmVuY2VzCiMgIGZvciBub3cgbGVhdmluZyB0aGUgUHJlL1BybyBCLWNlbGxzIG91dApzaGFyZWRfY2VsbHR5cGVzX2RmIDwtIHRpYmJsZTo6dHJpYmJsZSgKICB+aHBjYV9jZWxsdHlwZSwgfmJsdWVwcmludF9jZWxsdHlwZSwKICAjIEJvdGggcmVmZXJlbmNlcyBoYXZlIGl0OgogICJBc3Ryb2N5dGUiLCAiQXN0cm9jeXRlcyIsCiAgIkJfY2VsbCIsICJCLWNlbGxzIiwKICAiQ2hvbmRyb2N5dGVzIiwiQ2hvbmRyb2N5dGVzIiwKICAiREMiLCAiREMiLCAKICAiRW5kb3RoZWxpYWxfY2VsbHMiLCAiRW5kb3RoZWxpYWwgY2VsbHMiLCAKICAiRXBpdGhlbGlhbF9jZWxscyIsICJFcGl0aGVsaWFsIGNlbGxzIiwgCiAgIkZpYnJvYmxhc3RzIiwgIkZpYnJvYmxhc3RzIiwgCiAgIktlcmF0aW5vY3l0ZXMiLCAiS2VyYXRpbm9jeXRlcyIsIAogICJNYWNyb3BoYWdlIiwgIk1hY3JvcGhhZ2VzIiwgCiAgIk1vbm9jeXRlIiwgIk1vbm9jeXRlcyIsIAogICJOZXVyb25zIiwgIk5ldXJvbnMiLCAKICAiTmV1dHJvcGhpbHMiLCAiTmV1dHJvcGhpbHMiLCAKICAiTktfY2VsbCIsICJOSyBjZWxscyIsIAogICJTbW9vdGhfbXVzY2xlX2NlbGxzIiwgIlNtb290aCBtdXNjbGUiLCAKICAjIHR3byBncm91cHMgLSBjb2xsYXBzZSBiYWNrIHRvIG92ZXJhbGwgVCBjZWxscwogICJUX2NlbGxzIiwgIkNEOCsgVC1jZWxscyIsCiAgIlRfY2VsbHMiLCAiQ0Q0KyBULWNlbGxzIiwKICAjIHR3byBncm91cHMgb2YgSFNDcywgYWdhaW4sIGNvbGxhcHNlIGJhY2sgdG8gb3ZlcmFsbAogICJIU0NfLUctQ1NGIiwgIkhTQyIsIAogICJIU0NfQ0QzNCsiLCAiSFNDIgopICU+JQogIGRwbHlyOjptdXRhdGUoaGFybW9uaXplZF9jZWxsdHlwZSA9IGlmZWxzZShibHVlcHJpbnRfY2VsbHR5cGUgPT0gIkhTQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsdWVwcmludF9jZWxsdHlwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhwY2FfY2VsbHR5cGUpKQoKaGFybW9uaXplX2NlbGx0eXBlcyA8LSBmdW5jdGlvbihwcmVkc19kZiwgcmVmZXJlbmNlX25hbWUpIHsKICBpZiAocmVmZXJlbmNlX25hbWUgPT0gImhwY2EiKSB7CiAgICBzaGFyZWRfY2VsbHR5cGVzX2NvbG5hbWUgPC0gcmxhbmc6OnN5bSgiaHBjYV9jZWxsdHlwZSIpCiAgfSBlbHNlIHsKICAgIHNoYXJlZF9jZWxsdHlwZXNfY29sbmFtZSA8LSBybGFuZzo6c3ltKCJibHVlcHJpbnRfY2VsbHR5cGUiKQogIH0KICAKICBmaWx0ZXJlZF9wcmVkc19kZiA8LSBwcmVkc19kZiAlPiUKICAgIGRwbHlyOjpmaWx0ZXIocmVmZXJlbmNlID09IHJlZmVyZW5jZV9uYW1lKSAKICAKICBzeW1fcmVmZXJlbmNlX25hbWUgPC0gcmxhbmc6OnN5bShyZWZlcmVuY2VfbmFtZSkKICBmaWx0ZXJlZF9wcmVkc19kZiAlPiUKICAgIGRwbHlyOjppbm5lcl9qb2luKAogICAgICBkcGx5cjo6c2VsZWN0KAogICAgICAgIHNoYXJlZF9jZWxsdHlwZXNfZGYsIAogICAgICAgIGNlbGx0eXBlID0ge3tzaGFyZWRfY2VsbHR5cGVzX2NvbG5hbWV9fSwKICAgICAgICBoYXJtb25pemVkX2NlbGx0eXBlCiAgICAgICkKICAgICkgJT4lIAogICAgZHBseXI6OnNlbGVjdChjZWxsX2JhcmNvZGUsIHt7c3ltX3JlZmVyZW5jZV9uYW1lfX0gOj0gaGFybW9uaXplZF9jZWxsdHlwZSkgJT4lCiAgICBkcGx5cjo6cmlnaHRfam9pbihmaWx0ZXJlZF9wcmVkc19kZikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKHt7cmVmZXJlbmNlX25hbWV9fSA6PSBkcGx5cjo6aWZfZWxzZSgKICAgICAge3tzeW1fcmVmZXJlbmNlX25hbWV9fSAlaW4lIHNoYXJlZF9jZWxsdHlwZXNfZGYkaGFybW9uaXplZF9jZWxsdHlwZSwKICAgICAge3tzeW1fcmVmZXJlbmNlX25hbWV9fSwKICAgICAgY2VsbHR5cGUKICAgICkpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoLWNlbGx0eXBlLCAtcmVmZXJlbmNlKQogIH0KICAKcHJlZHNfZGZfaGFybW9uaXplZCA8LSBoYXJtb25pemVfY2VsbHR5cGVzKHByZWRzX2RmLCAiYmx1ZXByaW50X2VuY29kZSIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oCiAgICBoYXJtb25pemVfY2VsbHR5cGVzKHByZWRzX2RmLCAiaHBjYSIpICAKICApCgojIEhvdyBvZnRlbiBkbyB0aGV5IG1hdGNoPwoKcHJlZHNfZGZfaGFybW9uaXplZCAlPiUKICBkcGx5cjo6c3VtbWFyaXplKHBlcmNlbnRfc2FtZSA9IHN1bShibHVlcHJpbnRfZW5jb2RlID09IGhwY2EpIC8gZHBseXI6Om4oKSApCgoKIyBXaGVyZSBkbyB0aGV5IG5vdCBtYXRjaD8KcHJlZHNfZGZfaGFybW9uaXplZCAlPiUKICBkcGx5cjo6ZmlsdGVyKGhwY2EgIT0gYmx1ZXByaW50X2VuY29kZSkKICAKIyBBcmUgdGhlcmUgc3BlY2lmaWMgY29tYmluYXRpb25zIHRoYXQgZ2V0IGRpZmZlcmVudGx5IGFubm90YXRlZD8KcHJlZHNfZGZfaGFybW9uaXplZCAlPiUKICBkcGx5cjo6ZmlsdGVyKGhwY2EgIT0gYmx1ZXByaW50X2VuY29kZSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtY2VsbF9iYXJjb2RlKSAlPiUKICBkcGx5cjo6Y291bnQoYmx1ZXByaW50X2VuY29kZSwgaHBjYSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLW4pCgpgYGAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbl9pbmZvLCBldmFsPVRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCg==